<!--
 * @Author: 涵崽
 * @Date: 2023-12-06 16:10:44
 * @email: 1045654@qq.com
 * @gitee: https://gitee.com/han-zai
 * @LastEditors: 涵崽
 * @LastEditTime: 2023-12-06 16:11:50
 * @Description: 微信：1045654 
    
 实现原理:
布局：把需要做动画的容器设置高一点，越高你的动画内容呈现在视口上就越久，容器内部又有一个视口，而这个视口是sticky定位的，高度就是整个浏览器视口的高度
js: 样式根据滚动条均匀变化，而又有边界值（在滚动条到某处才开始和在某处才结束），得出这个变化的函数类似为f(x)= kx +b，这样我们可以根据滚动的高度进行计算出对应的属性值，比如滚动到2000了，需要得到滚动条滚动到2000时opacity的值那么就代入f(2000)即可，transform同理
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <header><h1>Header</h1></header>
    <div class="container-wraper">
      <!--动画区域-->
      <div class="ani-container">
        <!--视口-->
        <ul class="list">
          <li order="0" class="item">H</li>
          <li order="1" class="item">H</li>
          <li order="2" class="item">H</li>
          <li order="3" class="item">H</li>
          <li order="2" class="item">H</li>
          <li order="1" class="item">H</li>
          <li order="0" class="item">H</li>
          <li order="0" class="item">H</li>
          <li order="1" class="item">H</li>
          <li order="2" class="item">H</li>
          <li order="3" class="item">H</li>
          <li order="2" class="item">H</li>
          <li order="1" class="item">H</li>
          <li order="0" class="item">H</li>
        </ul>
      </div>
    </div>
    <footer><h1>Footer</h1></footer>

    <script>
      const items = document.querySelectorAll(".item");
      const containerWraper = document.querySelector(".container-wraper");
      const list = document.querySelector(".list");

      // 主要函数，类似函数f(x) = kx + b 得出最终的y
      function fx(minX, maxX, minY, maxY) {
        return function (x) {
          if (x < minX) {
            return minY;
          }
          if (x > maxX) {
            return maxY;
          }

          // 类似函数y = kx + b

          return ((maxY - minY) / (maxX - minX)) * (x - minX) + minY;
        };
      }

      //节点以及对应的样式属性
      const propObj = new Map(); // dom作为属性， 对象为值， 对象中 需要更新的样式属性名作为对象的属性名，结果作为值

      // 辅助方法，生成每个节点应该更新的属性
      function getDomStylePropValue(dom, minX, maxX) {
        // 添加顺序，不同的x决定y的变化
        minX = minX + dom.getAttribute("order") * 300;

        const opacity = fx(minX, maxX, 0, 1);

        const scale = fx(minX, maxX, 0, 1);
        const tx = fx(
          minX,
          maxX,
          list.clientWidth / 2 - dom.offsetLeft - dom.clientWidth / 2,
          0
        );
        const ty = fx(
          minX,
          maxX,
          list.clientHeight / 2 - dom.offsetTop - dom.clientHeight / 2,
          0
        );
        const transform = function (x) {
          return `translate(${tx(x)}px, ${ty(x)}px) scale(${scale(x)})`;
        };

        return {
          opacity,
          transform,
        };
      }

      // 填充propObj
      function fillPropToObj() {
        propObj.clear();
        const containerWraperRect = containerWraper.getBoundingClientRect(); // 获取容器位置以及几何信息
        const minX = containerWraperRect.top + window.scrollY; // 容器containerWraper触摸到顶部，开始有变化
        const maxX =
          containerWraperRect.bottom + window.scrollY - window.innerHeight; // 容器containerWraper底部触摸到可视窗口底部开始不再变化

        items.forEach(dom => {
          propObj.set(dom, getDomStylePropValue(dom, minX, maxX));
        });
      }

      // 更新样式属性
      function updateStyleAttr() {
        for (const [dom, value] of propObj) {
          Object.keys(value).forEach(prop => {
            dom.style[prop] = value[prop](window.scrollY);
            console.log(prop, value[prop](window.scrollY));
          });
        }
      }

      fillPropToObj();
      updateStyleAttr();

      console.log(propObj);

      window.addEventListener("scroll", updateStyleAttr);
    </script>
  </body>
</html>
